package IO;


import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

public class parser {
	
	static String levelReader (String location) throws IOException
	{
		String unconverted;
		unconverted = readLevel(location);
		System.out.println("Succesfully loaded");
		return unconverted;
		
	}
	
	private static String readLevel(String filePath) throws IOException
	{
	    byte[] buffer = new byte[(int) new File(filePath).length()];
	    BufferedInputStream f = null;
	    try {
	        f = new BufferedInputStream(new FileInputStream(filePath));
	        f.read(buffer);
	    } finally {
	        if (f != null) 
	        	try 
	        {
	        		f.close(); 
	        } 
	        catch  (IOException ignored) 
	        {
	        }
	    }
	    return new String(buffer);
	}
	
	static char[][] levelConverter (String unconverted, level caller) 
	{
		char[][] level;
		if(unconverted == "")
		{
			level = new char[0][0];
			return level = null;                          
		}
		int x = 0, y = 0, z = 0;
		char current;
		int width = getWidth(unconverted); 
		level = new char[width][(unconverted.length() / width)-1]; // init char[][] array
		
		// fill level according to the proposed data model. If it encounters an unknown symbol, it will 
		// just assign null to level, which will be checked for in the level class.
		while(z < unconverted.length())
		{
			current = unconverted.charAt(z);
			System.out.println(current);
			if(current == 'S' || current == 'X' || current == '<' || current == 'v' || current == '>' || current == '^' || current == '_' || current == '#' ||current == 't')
			{
				level[x][y] = current;
				y++; 
			}
			else if (current =='\n')
			{
				y=0;
				x++;
			}
			else
			{
				System.out.println("Level null because unrecognised character");
				level = null;
				return level;
			}
			z++;
		}
		
		// Validates the level
		if(levelValidator(level, caller) == true)
		{
		
		for(int i=0;i<level.length;i++){
			for(int j=0;j<level[0].length; j++)
				System.out.print(level[i][j]);
		System.out.print('\n');
		}
		return level;
		}
		else
		{
			System.out.println("Level null cause checks failed");
			level = null;
			return level;
		}
	}

	static int getWidth(String unconverted) {
		int y = 0, width = 0;
		char current;
		
		while(width == 0)
		{
			current = unconverted.charAt(y);
			if(current == '\n')
			{
				width = y;
			}
			else
			{
				y++;
			}
		}
		System.out.println("Width is: " + width);
		return width;
	}

	static boolean levelValidator (char[][] level, level caller)
	{
		boolean isValid = true;
		
		System.out.println("Validating level");
		char[][] mymap = new char[level.length][level[0].length];
		for(int i = 0;i<level[0].length;i++)
			for(int j = 0;j<level.length;j++)
			{
				mymap[i][j] = level[j][i];
			}
		if (level != null)
		{
			if(spawnChecker(level) == false || goalChecker(level) == false || pathChecker(mymap, caller) == false)
			{
				isValid = false;
				System.out.println("invalid because checks failed");
			}
		}
		else
			{
				isValid = false;
				System.out.println("invalid because level empty");
			}
		
		return isValid;
	}
	
	static boolean spawnChecker (char[][] level)
	{
		System.out.println("Entered SpawnChecker");
		int numOfSpawns = 0;
		int x ,y = 0;
		
		do
		{
			x = 0;
			do
			{
				if(level[x][y] == 'S')
				{
					numOfSpawns++;
					
				}
				x++;
			}while(x < level.length);
			y++;
		}while(y < level[0].length);
		
		
		if (numOfSpawns > 0)
		{
			System.out.println("Exiting SpawnChecker, true");
			System.out.println(numOfSpawns);
			return true;
		}
		else
		{
			System.out.println("Exiting SpawnChecker, false");
			return false;
		}
	}
	
	static boolean goalChecker (char[][] level)
	{
		System.out.println("Entering goalChecker");
		int numOfGoals = 0;
		int x, y = 0;
		
		do
		{
			x = 0;
			do
			{
				if(level[x][y] == 'X')
				{

					numOfGoals++;
				}
				x++;
			}while(x < level.length);
			y++;
			
		}while(y < level[0].length);
		
		
		if (numOfGoals == 1)
		{
			System.out.println("Exiting goalChecker, true");
			return true;
		}
		else
		{
			System.out.println("Exiting goalChecker, false");
			return false;
		}
	}
	
	static boolean pathChecker (char[][] level, level caller)
	{
		System.out.println("Entering pathChecker");
		int numOfSpawns = 0, iteration = 0;
		Waypoint position;
		Waypoint goal = getGoal(level);
		Path Waypath;
		ArrayList<Waypoint> spawns = getSpawns(level);
		ArrayList<Path> paths = new ArrayList<Path>();
		
		
		numOfSpawns = spawns.size(); // was spawns.size() / 2 --- but why?
		
		System.out.println("numOfSpawns: " + numOfSpawns);
		System.out.println("spawns.size(): " + spawns.size());
		
		while(iteration < numOfSpawns)
		{
			System.out.println("Entering path search loop");
			// The spawn point for the current iteration
			position = spawns.get(iteration);
			// initialises the Path object in the Path-Arraylist with the Spawn-Point
			Waypath = new Path(position);
			
			// checks the fields surrounding spawn to see in which direct to go first
			if(level[position.getX()+1][position.getY()] == '>' || level[position.getX()+1][position.getY()] == '<' || level[position.getX()+1][position.getY()] == '^' || level[position.getX()+1][position.getY()] == 'v')
			{
				position.move('>');
				System.out.println("Field to the right of spawn");
			}
			else if(level[position.getX() - 1][position.getY()] == '>' || level[position.getX() - 1][position.getY()] == '<' || level[position.getX() - 1][position.getY()] == '^' || level[position.getX() - 1][position.getY()] == 'v')
			{
				System.out.println("Field to the left of spawn");
				position.move('<');
			}
			else if(level[position.getX()][position.getY() + 1] == '>' || level[position.getX()][position.getY() + 1] == '<' || level[position.getX()][position.getY() + 1] == '^' || level[position.getX()][position.getY() + 1] == 'v')
			{
				System.out.println("Field below spawn");
				position.move('v');
			}
			else if(level[position.getX()][position.getY() - 1] == '>' || level[position.getX()][position.getY() - 1] == '<' || level[position.getX()][position.getY() - 1] == '^' || level[position.getX()][position.getY() - 1] == 'v')
			{
				System.out.println("Field above spawn");
				position.move('^');
			}
			else
			{
				System.out.println("No valid field next to spawn");
				return false;
			}
			
			while(position.getX() != goal.getX() || position.getY() != goal.getY())
			{
				System.out.println("Current Position: X/Y" + position.getX() +"/"+ position.getY());
				System.out.println(level[position.getX()][position.getY()]);
				if(position.getX() < 0 || position.getY() < 0 || position.getX() > level.length || position.getY() > level[0].length)
				{
					System.out.println("out of bounds");
					return false;
				}
				
				if(Waypath.searchWaypoint(position) == true)
				{
					System.out.println("Added to Waypath");
					Waypath.addWaypoint(position);
				}
				else
				{
					System.out.println("Waypoints create Loop");
					return false;
				}
				if(level[position.getX()][position.getY()] == '>')
				{
					System.out.println("Moving right");
					position.move('>');
				}
				else if(level[position.getX()][position.getY()] == '<')
				{
					System.out.println("Moving left");
					position.move('<');
				}
				else if(level[position.getX()][position.getY()] == '^')
				{
					System.out.println("Moving up");
					position.move('^');
				}
				else if(level[position.getX()][position.getY()] == 'v')
				{
					System.out.println("Moving down");
					position.move('v');
				}
				else
				{
					System.out.println("Landed on a field away from the path");
					return false;
				}
			}
			
			System.out.println("Goal reached");
			System.out.println("Current Position: X/Y" + position.getX() +"/"+ position.getY());
			System.out.println(level[position.getX()][position.getY()]);
			System.out.println("Current goal Position: X/Y" + goal.getX() +"/"+ goal.getY());
			System.out.println(level[goal.getX()][goal.getY()]);
			
			
			Waypath.addWaypoint(position);
			paths.add(Waypath);
			iteration++;
		}
		System.out.println("Exiting pathChecker, true");
		caller.setPaths(paths);
		return true;
	}

	static Waypoint getGoal (char[][] level)
	{
		Waypoint goal = null;
		
		int x, y = 0;
		
		do
		{
			x = 0;
			do
			{
				if(level[x][y] == 'X')
				{
					goal = new Waypoint(x, y);				
				}
				x++;
			}while(x < level.length && goal == null);
			y++;
			
		}while(y < level[0].length && goal == null);
		
		System.out.println("Goal is at: X/Y " + goal.getX() +"/"+ goal.getY());
		return goal;
		
	}
	
	static ArrayList<Waypoint> getSpawns (char[][] level)
	{
		ArrayList<Waypoint> spawns = new ArrayList<Waypoint>();
		int x, y = 0;
		
		do
		{
			x = 0;
			do
			{
				if(level[x][y] == 'S')
				{
					spawns.add(new Waypoint(x, y));			
				}
				x++;
			}while(x < level.length);
			y++;
			
		}while(y < level[0].length);
		
		return spawns;
	}

	static void toStringAndSave(char[][] level, String name) throws IOException
	{
		int x = 0, y = 0;
		String converted = "";
		if (name == null)
		{
			name = "C:/default.txt";
		}
		FileWriter writer = new FileWriter(name);
		
		do
		{
			do
			{
				converted += level[x][y];
				x++;
			}while(x < level.length);
			converted += '\n';
			x = 0;
			y++;
		}while(y < level[0].length);	
		writer.write(converted);
		writer.close();
	}

	public static String toString(char[][] level) {
		int x = 0, y = 0;
		String converted = "";
		
		if(level != null)
		{
		do
		{
			do
			{
				converted += level[x][y];
				x++;
			}while(x < level.length);
			converted += '\n';
			x = 0;
			y++;
		}while(y < level[0].length);
		}
		
		return converted;
	}

}

